/*
asio  解决粘包问题 - 简化切包逻辑
async_read_some 这个函数的特点是只要对端发数据，服务器接收到数据，即使没有收全对端发送的数据也会触发
那么可以采用async_read函数，这个函数指定读取指定字节数，只有完全读完才会触发回调函数。
*/

#include <boost/asio/detail/socket_ops.hpp>
#include <boost/asio/io_context.hpp>
#include <cstdint>
#include <utility>
#include <memory>
#include <string.h>
#include <mutex>
#include <string>
#include <queue>
#include <map>

#include <boost/asio.hpp>
#include <boost/uuid/uuid_generators.hpp>
#include <boost/uuid/uuid_io.hpp>

using boost::asio::ip::tcp;

constexpr uint32_t HEAD_LENGTH = sizeof(uint32_t);
constexpr uint32_t MAX_SEND_NUM = 1024;

class MsgNode
{
    friend class Session;
public:
    //用于发送数据
    MsgNode(char* data, uint32_t len)
    :m_cur_len(0)
    ,m_max_len(len + HEAD_LENGTH)
    ,m_data(new char[m_max_len + 1])
    {
        uint32_t net_len = boost::asio::detail::socket_ops::host_to_network_long(len);
        memcpy(m_data.get(), &net_len, HEAD_LENGTH);
        memcpy(m_data.get() + HEAD_LENGTH, data, len);
        m_data[m_max_len] = '\0';
    }

    //用于接收数据
    MsgNode(uint32_t len)
    :m_cur_len(0)
    ,m_max_len(len)
    ,m_data(new char[m_max_len + 1]) 
    {
        m_data[m_max_len] = '\0';
    }

    uint32_t GetHeadNum()
    {
        if(m_max_len < HEAD_LENGTH)
        {
            return 0;
        }

        uint32_t msg_len = 0;
        memcpy(&msg_len, m_data.get(), HEAD_LENGTH);
        msg_len = boost::asio::detail::socket_ops::network_to_host_long(msg_len);
        return msg_len;
    }

    void Clear()
    {
        memset(m_data.get(), 0, m_max_len);
        m_cur_len = 0;
    }
private:
    uint32_t m_cur_len;   //已经处理的数据长度
    uint32_t m_max_len;   //数据总长度
    std::unique_ptr<char[]> m_data;
};


class Server;

class Session: public std::enable_shared_from_this<Session>
{
public:
    Session(boost::asio::io_context& ioc, Server* server)
    :m_socket(ioc)
    ,m_server(server)
    ,m_recv_head_node(new MsgNode(HEAD_LENGTH))
    ,m_head_parse(false)
    {
        m_uuid = boost::uuids::to_string(boost::uuids::random_generator()());
    }

    void Start();
    void Send(char* msg, size_t len);
    tcp::socket& GetSocket() { return m_socket;}
    const std::string& GetUuid() const { return m_uuid; }
    void PrintRecvData(char* data, uint32_t len); //二进制形式打印数据
private:

    void _HadnleReadHead(const boost::system::error_code& ec, size_t len, std::shared_ptr<Session> self_ptr);
    void _HadnleReadMsg(const boost::system::error_code& ec, size_t len, std::shared_ptr<Session> self_ptr);
    void _HandleWrite(const boost::system::error_code& ec, std::shared_ptr<Session> self_ptr);
    
private:
    const uint32_t m_max_len = 1024;

    tcp::socket m_socket;   //客户端socket
    Server*     m_server;   //server
    std::string m_uuid;     //唯一标示
    std::mutex  m_send_lock;
    std::queue<std::shared_ptr<MsgNode>> m_send_que; //发送队列
    
    std::shared_ptr<MsgNode> m_recv_msg_node;   //消息体缓冲区
    std::shared_ptr<MsgNode> m_recv_head_node;  //消息头缓冲区
    bool m_head_parse;     //消息头是否解析完成
};

class Server
{
public:

    Server(boost::asio::io_context&ioc, uint16_t port);
    void DeleteSession(const std::string& uuid);
private:    

    //使用智能指针解决程序崩溃问题
    void _StartAccetp();
    void _HandleAccept(std::shared_ptr<Session> session, const boost::system::error_code& ec);
private:
    boost::asio::io_context& m_ioc; //io上下文 不可以拷贝，只能引用
    tcp::acceptor m_acceptor;       //监听客户端的连接

    std::map<std::string, std::shared_ptr<Session>> m_sessions; 
    std::mutex m_mutex;
};